Implement a generic
First<T>
that takes an ArrayT
and returns its first element's type.
實現一個泛型 First<T>
,它接受一個數組 T
,並返回其第一個元素的類型。
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
接下來,你的任務是讓下面的type cases測試通過:
type cases = [
Expect<Equal<First<[3, 2, 1]>, 3>>,
Expect<Equal<First<[() => 123, { a: string }]>, () => 123>>,
Expect<Equal<First<[]>, never>>,
Expect<Equal<First<[undefined]>, undefined>>,
]
在這一關中,我們可以從以下幾個方向來思考:
T
是一個 array ?我們將會用到:
extends 在第三關已經介紹過,可以回到第三關參考喔!
解法1:
type First<T extends any[]> = T extends []? never : T[0]
細節分析:
T extends any[]
:此部分確保 T
必須是一個陣列類型,任何陣列都適用,不限定具體類型。T extends []
:這裡使用條件類型檢查 T
是否是一個空陣列。如果是空陣列,就返回 never
,表示沒有元素。T[0]
:如果 T
不是空陣列,則返回陣列的第一個元素類型。T[0
] 是 TypeScript 用來取得陣列第一個元素的標準方式。解法2:
type First<T extends any[]> = T['length'] extends 0? never : T[0]
細節分析:
T['length']
:T['length']
取得陣列的長度。這是一種靜態檢查陣列是否為空的方式。這裡我們通過訪問 T
的 length
屬性來獲取數組的長度。TypeScript 的數組類型都有一個隱式的 length
屬性,這表示數組中元素的數量。
T['length'] extends 0
:這裡檢查陣列長度是否為 0
。如果是,則返回 never
,表示空陣列。
T[0]
:當陣列不為空時,返回第一個元素的類型。這是用來獲取非空陣列第一個元素的標準方式。
解法3:
type First<T extends any[]> = T extends {length:0} ? never : T[0]
細節分析:
T extends { length: 0 }
:使用條件類型來檢查 T
是否是一個長度為 0 的陣列。透過直接檢查 T
是否符合具有 length
屬性且該屬性為 0
的結構。如果長度為 0
,返回 never
。
T[0]
:和之前的解法類似,當陣列不為空時,直接返回第一個元素的類型。
解法4:
type First<T extends any[]> = T extends [infer P, ...any[]] ? P : never
細節分析:
infer P
: 使用 infer 關鍵字從元組的第一個元素中提取類型 P。這樣可以靈活地獲取數組中的第一個元素。...any[]
: 表示剩餘的元素可以是任何類型,這樣可以匹配任何長度的數組。P
:如果 T 是一個包含元素的數組,返回第一個元素的類型;否則返回 never。這樣,我們就能順利通過測試啦 🎉 😭 🎉
infer
keyword in TypeScript allows you to declare a type variable within a conditional type. When used in the context of a conditional type, infer
tells TypeScript to:
type ElementType<T> = T extends (infer E)[] ? E : never;
infer E
tells TypeScript to infer the type of the array element and assign it to the type variable E
.T
matches the pattern (infer E)[]
, TypeScript infers the type of E
based on the type of elements in the array T
.type ArrayType1 = ElementType<number[]>; // ArrayType1 is number
type ArrayType2 = ElementType<string[]>; // ArrayType2 is string
function format(value: string | number) {
if (typeof value === 'string') {
return value.trim();
} else {
return value.toFixed(2); // we're sure it's number
}
// not a string or number
// "value" can't occur here, so it's type "never"
}
The return type of this function is never, indicating that it never produces a normal value. Instead, it always throws an exception, which prevents the function from reaching its end point:
// Function returning never must not have a reachable end point
function error(message: string): never {
throw new Error(message);
}
const reportError = function () {
throw Error('my error');
}
// reportError's type is () => never.
error
function has a return type of never
, the return type of fail
function is inferred to be never
as well.fail
function itself doesn't explicitly throw an error, it calls error
, which is a never
returning function:// Inferred return type is never
function fail() {
return error("Something failed");
}
while (true) {}
), which means it never terminates normally.never
, indicating that it never produces a normal value. Instead, it continues running indefinitely. // Function returning never must not have a reachable end point
function infiniteLoop(): never {
while (true) {}
}
const loop = function () {
while(true) {}
}
// loop's type is () => never.
本次介紹了 First of Array
的實作,下一關會挑戰 Length of Tuple
,期待再相見!